package com.chipcoo.shiro.filters;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chipcoo.models.gen.ConfRedirectRule;
import com.chipcoo.models.gen.ConfSiteMatcher;
import com.chipcoo.models.gen.Finger;
import com.chipcoo.services.ConfService;
import com.chipcoo.utils.SecurityUtils;
import com.chipcoo.utils.StringUtils;
import com.chipcoo.ResponseResult;
import com.chipcoo.web.utils.MediaTypeUtils;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import static com.chipcoo.web.utils.WebUtils.accessControl;
import static com.chipcoo.web.utils.WebUtils.isHttpOptions;

public class CoreFilter extends OncePerRequestFilter {
    public static final String ATTRIBUTE_KEY_CONF_SITE = "_conf_site_matcher";
    @Autowired
    private ConfService confService;

    @Override
    protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {

        if(request instanceof HttpServletRequest) {

            ConfSiteMatcher siteMatcher = com.chipcoo.web.utils.WebUtils.getCurrentSite(request);
            if (null == siteMatcher) ((HttpServletResponse) response).sendError(HttpStatus.BAD_REQUEST.value());
            else {
                HttpServletRequest req = (HttpServletRequest) request;

                req.setAttribute(ATTRIBUTE_KEY_CONF_SITE, siteMatcher);
                HttpServletResponse res = (HttpServletResponse) response;


                if (!shouldPrintFinger()) {
                    if( com.chipcoo.web.utils.WebUtils.ACCESS_CONTROL_ALLOW_ORIGINS ) {
                        accessControl(req,res);
                    }

                    if( isHttpOptions(req) ){
                        res.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE");

                        res.setStatus(200);
                    }
                    else chain.doFilter(request, response);  //继续执行链路
                } else {
                    String userIP = com.chipcoo.web.utils.WebUtils.getClientIP(req);
                    Finger finger = SecurityUtils.getCurrentFinger();
                    if(null==finger) finger = SecurityUtils.parseDeviceFinger(req.getHeader("User-Agent"), null, null);
                    String textUID = com.chipcoo.web.utils.WebUtils.getCookie(req, "uid");
                    int userID;
                    if (null == textUID) userID = 0;
                    else userID = StringUtils.radix642i(textUID);

                    ConfRedirectRule confRedirectRule = checkRedirect(req, res, userID, userIP, finger);

                    if (null != confRedirectRule) {
                        WebUtils.issueRedirect(request, response, confRedirectRule.getUrlRedirect(), null, false, true);
                    } else {
                        String returnUrl = String.format("%s%s%s",req.getRequestURI()
                                ,null==req.getQueryString()?"":"?"
                                ,null==req.getQueryString()?"":req.getQueryString() );
                        ResponseResult responseResult = ResponseResult.createError(ResponseResult.REQUIRED_FINGER)
                                .put(ResponseResult.KEY_REDIRECT,"/passport/fingerprint");
                        if(null!=returnUrl)
                            responseResult.put("returnUrl", returnUrl);

                        com.chipcoo.web.utils.WebUtils.responseEnd(req,res,responseResult);

                        HashMap<String, String> map = null;
                        if (null != returnUrl) {
                            map = new HashMap<String, String>();
                            map.put("returnUrl", returnUrl);
                        }

                    }
                }
            }
        }else chain.doFilter(request, response);
    }


    private boolean shouldPrintFinger()
    {
        boolean gotoFingerPrint = false;
        if( null == SecurityUtils.getSession(false) )
        {
            gotoFingerPrint = true;
        }
        return gotoFingerPrint;
    }

    private final static class CheckResult{
        private  int flags;
        private int results;
        private boolean isMatched(){
            if(flags==0) return true;
            return  (flags&results)>0;
        }
    }
    private void checkCommon(CheckResult result, JSONObject jo, String key, int flag, String currentVal){
        if(null!=currentVal) {
            if (result.isMatched() && jo.containsKey(key)) {
                String szText = jo.getString(key);
                if (null != szText) {
                    szText = szText.trim();
                    if (szText.length() > 0) {
                        result.flags |= flag;
                        if( szText.contains(String.format("|%s|",currentVal))){
                            result.results|=flag;
                        }
                    }
                }
            }
        }
    }
    //region 拦截BLOCKED 的用户
    private boolean isBeenBlocked(ConfRedirectRule item,HttpServletRequest req,HttpServletResponse res
            ,Integer userID
            ,String userIP
            ,Finger finger
    ) {

        String cookieValue = com.chipcoo.web.utils.WebUtils.getCookie(req, item.getCookieName());
        if (!StringUtils.isEmpty(cookieValue)) return true;
        else {
            JSONObject jo = JSON.parseObject(item.getMatchSchema());
            CheckResult checkResult = new CheckResult();
            if (jo.containsKey("ip")) {
                //region 判断IP是否被禁用 ip 1
                String szText = jo.getString("ip");
                if (null != szText) {
                    szText = szText.trim();
                    if (szText.length() > 0) {
                        checkResult.flags |= 1;
                        String[] blockIPList = szText.split(",");
                        for (int i = 0; i < blockIPList.length; i++) {
                            if (blockIPList[i].length() > 0) {
                                if (StringUtils.ip1belongip2(userIP, blockIPList[i])) {
                                    checkResult.results |= 1;
                                    break;
                                }
                            }
                        }
                    }
                }
                //endregion
            }


            if(userID>0) checkCommon(checkResult, jo, "uid", 2, Integer.toString(userID)); //判断用户ID uid 2

            if (null != finger) {
                checkCommon(checkResult, jo, "botFaker", 4, Integer.toString(finger.getBotFaker())); //判断机器人标志 botFaker 4
                checkCommon(checkResult, jo, "appFaker", 8, Integer.toString(finger.getAppFaker())); //判断国产浏览器 appFaker 8
                checkCommon(checkResult, jo, "app", 16, Integer.toString(finger.getApp())); //判断应用程序 app 16
                checkCommon(checkResult, jo, "appVersion", 32, Float.toString(finger.getAppVersion()));

                checkCommon(checkResult, jo, "os", 64, Integer.toString(finger.getOs()));
                checkCommon(checkResult, jo, "osVersion", 128, Float.toString(finger.getOsVersion()));

            }



            if ( (checkResult.flags&checkResult.results)>0) {
                com.chipcoo.web.utils.WebUtils.setCookie(res, item.getCookieName(), item.getId().toString(), 0x7FFFFFFF);
                return true;
            } else return false;
        }
    }
    private ConfRedirectRule checkRedirect(HttpServletRequest req,HttpServletResponse res
            ,Integer userID
            ,String userIP
            ,Finger finger){
        List<ConfRedirectRule> redirectRules = confService.selectRedirectRule();
        ConfRedirectRule found = null;
        for (int i = 0; i < redirectRules.size(); i++) {
            ConfRedirectRule item = redirectRules.get(i);
            if (isBeenBlocked(item,req,res,userID,userIP,finger)) {
                found = item;
                break;
            }
        }
        return  found;
    }
    //endregion
}
